/*
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see http://www.gnu.org/licenses/. 
 *
 *  Authors:
 *  	Albert Marieges Garcia
 *  	Álex Pardo Fernández
 *  	Enric Mayas Márquez
 *  	Juan Carlos Calvo Tejedor
 *  	Marc Gironés Dezsényi
 *  
 *  Contact:
 *  	bcn.tours.android@gmail.com
 *  Webpage:
 *  	bcntours.co.cc
 *
 */
package com.BCNTours.AR.Vista;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationProvider;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Display;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import com.BCNTours.R;
import com.BCNTours.AR.Utils.ARLocationManager;
import com.BCNTours.AR.Utils.Coordinates;
import com.BCNTours.AR.Utils.Quicksort;
import com.BCNTours.Model.ApplicationData;
import com.BCNTours.Model.Attraction;
import com.BCNTours.Model.InformationPoint;
import com.BCNTours.Model.Monument;
import com.BCNTours.Model.Museum;
import com.BCNTours.Model.Poi;
import com.BCNTours.Vista.BCNTours;
import com.BCNTours.Vista.PoiDialog;
import com.BCNTours.Vista.Preferences;

/**
 * This Class launches an A.R. view
 *
 */
public class ARactivity extends Activity {
	
	private CamPreview mPreview;
	private DrawRadar mRadar;
	private DrawSignal mDraw;
	
	private SensorManager mySensorManager;
	private ARLocationManager myLocationManager;
	private ApplicationData applicationData;
	private ArrayList<POIButton> poiButtons;
	
	private Location myLocation;
	private float myAzimuth;
	private float myPitch;
	
	private int units;
	private int distance;	
	private boolean distEnabled;
	private boolean firstCalled = true;
	
	/** 
	 * Called when the activity is first created. 
	 */
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setWindowOptions();      
        
        Bundle extras = getIntent().getExtras();
        applicationData = (ApplicationData) extras.get("com.ShowMe.Model.PoiList");
        distance = applicationData.getDistfilter();
        distEnabled = applicationData.isDistancefilterEnabled();
        
        
        // Create our Preview view and set it as the content of our activity.
        mPreview = new CamPreview(this);  
        mPreview.setOnTouchListener(myTouchListener);
        setContentView(mPreview);
        
        // Create radar view
        mRadar = new DrawRadar(this);
        addContentView(mRadar, new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT));
        
        //Create the signal marker
        mDraw = new DrawSignal(this);
        addContentView(mDraw, new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT));
        
        //Checks the language and the units
        checkPrefs();
        
        //Initialization the location
        initializationLocation();
        
        //Starting sensors
        initializationSensor();
        
        //Draw on the screen the interesting points that the user wishes to see.
//        addPOIButtons();
        BCNTours.loading.dismiss();
    }
    
    
    
	@Override
	protected void onResume() {
		Log.i("first", "reconnecting camera ARactivity");
		mPreview.reconnectCamera();
		super.onResume();
	}

	

	@Override
	protected void onPause() {
		try {
			mPreview.stopCamera();
			mPreview.releaseCamera();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		super.onPause();
	}

	

	/**
	 * Create and manage the distance dialog
	 */
	public void createDistaceDialog(){

	    final Dialog distanceDialog = new Dialog(ARactivity.this);
	    distanceDialog.setContentView(R.layout.distance_filter_dialog);
	    distanceDialog.setTitle(R.string.distanceDialog);
	
	    distanceDialog.setCancelable(true);
	
	    //Add the textview to the dialog
	    final TextView txtDistanceProgress = (TextView) distanceDialog.findViewById(R.id.txtDistanceProgress);
	    txtDistanceProgress.setText(Integer.toString(distance));
	    distance = applicationData.getDistfilter();
	    //Add the seekbar to the dialog
	    final SeekBar seekDistanceFilter = (SeekBar) distanceDialog.findViewById(R.id.seekDistanceFilter);
	    final CheckBox chkDistEnabled = (CheckBox) distanceDialog.findViewById(R.id.chkBxDisableDist);
	    chkDistEnabled.setChecked(!distEnabled);
	    if(!distEnabled){
	    	seekDistanceFilter.setEnabled(false);
	    }
		seekDistanceFilter.setProgress(distance-1);
		seekDistanceFilter.refreshDrawableState();
		chkDistEnabled.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
			
			@Override
			public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
				if(isChecked){
					seekDistanceFilter.setEnabled(false);
				} else{
					seekDistanceFilter.setEnabled(true);
				}
				
			}
		});
		seekDistanceFilter.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener(){
		    
	    	public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
		    // TODO Auto-generated method stub
		    	txtDistanceProgress.setText(Integer.toString(progress+1));
		    	distance = progress+1;
		    }
		    
		    public void onStartTrackingTouch(SeekBar seekBar) {
		    // TODO Auto-generated method stub
		    }
		
		    public void onStopTrackingTouch(SeekBar seekBar) {
		    // TODO Auto-generated method stub
		    	Log.i("change", "change(STOP)");
		    	distance = seekBar.getProgress()+1;
		    }
	    });
		
		//Add the cancel button to the distance dialog
	    Button btnDistanceCancel = (Button) distanceDialog.findViewById(R.id.btnDistanceCancel);
	    btnDistanceCancel.setOnClickListener(new OnClickListener() {
	        public void onClick(View v) {
	        	distanceDialog.cancel();
	        	distance = applicationData.getDistfilter();
	        }
	    });
	    
	  //Add the ok button to the distance dialog
	    Button btnDistanceOk = (Button) distanceDialog.findViewById(R.id.btnDistanceOk);
	    btnDistanceOk.setOnClickListener(new OnClickListener() {
	        public void onClick(View v) {
	        	if(chkDistEnabled.isChecked()){
	        		distanceDialog.cancel();
	        		applicationData.setDistfilter(distance);
	        		applicationData.DistancefilterEnabled(false);
	        		distEnabled = false;
	        	} else{
		        	distanceDialog.cancel();
		        	applicationData.setDistfilter(distance);
		        	applicationData.DistancefilterEnabled(true);
		        	distEnabled = true;
	        	}
	        }
	    });
	    distanceDialog.show();
	}

	private void checkPrefs() {
		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
		units = prefs.getInt(Preferences.PREF_UNITS, 0);
	}
    
   
    /**
     * Create a menu of the activity
     */
    public boolean onCreateOptionsMenu(Menu menu){
    	MenuInflater mi = getMenuInflater();
    	mi.inflate(R.menu.menucamera , menu);
    	return true;
    }
    
    /**
     * Listener from the menu
     */
    public boolean onOptionsItemSelected (MenuItem item){
    	switch (item.getItemId()){
    		//Distance menu
    		case R.id.dist_menu:
    			createDistaceDialog();
    			break;
    		
    		default:
    			Toast.makeText(getApplicationContext(), "Menu error!", Toast.LENGTH_LONG);
    			break;
    			
    	}
    	return false;
    }

	/**
	 * Sets the window options such as the no title option, landscape orientation, etc.
	 */
    private void setWindowOptions(){
    	requestWindowFeature(Window.FEATURE_NO_TITLE);
    	setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
    }
    
    /**
     * Draw on the screen the interesting points that the user wishes to see.
     */
    private void addPOIButtons(){
    	poiButtons = new ArrayList<POIButton>();
    	
    	//Get the list of the types of interesting points that the user wishes to see.
    	boolean [] filterPreferences = applicationData.getFilterPreferences();
    	for(int i = 0; i < filterPreferences.length; i++){
    		
    		//If the types of interesting points is true, draw those on the screen
    		if(filterPreferences[i]){
    	    	ArrayList<Object> params =  new ArrayList<Object>();
    	        Display display = getWindowManager().getDefaultDisplay();
    			
    			switch(i){
    				//If the types of interesting points is MONUMENT
    				case 0:
    					//Get the monuments points
    					ArrayList<Monument> monuments = applicationData.getMonuments();
    	    	        for(Monument tmpMon : monuments){
    	    	        	//Create the POI button with the monument information's
    	    	        	params =  new ArrayList<Object>();
    	    	            params.add(tmpMon.getId());
    	    	            params.add(new Coordinates(new Float(tmpMon.getGpsLat()),new Float(tmpMon.getGpsLong())));
//    	    	            params.add(new Coordinates(new Float(tmpMon.getGpsLong()),new Float(tmpMon.getGpsLat())));
    	    	            params.add(new Float(tmpMon.getGpsAlt()));
    	    	            params.add(tmpMon.getImage());
    	    	            params.add((float)display.getHeight());
    	    	            params.add((float)display.getWidth());
    	    	            params.add(tmpMon.getName());
    	    	            
    	    	            POIButton mButton = new POIButton(this, params);
    	    	            poiButtons.add(mButton);
    	    	        }    
    					break;
    					
    				//If the types of interesting points is ATTRACTION
    				case 1:
    					//Get the attraction points
    					ArrayList<Attraction> attracions = applicationData.getAttractions();
    	    	        for(Attraction tmpAttr : attracions){
    	    	        	//Create the POI button with the attraction information's
    	    	        	params =  new ArrayList<Object>();
    	    	            params.add(tmpAttr.getId());
    	    	            params.add(new Coordinates(new Float(tmpAttr.getGpsLat()),new Float(tmpAttr.getGpsLong())));
//    	    	            params.add(new Coordinates(new Float(tmpAttr.getGpsLong()),new Float(tmpAttr.getGpsLat())));
    	    	            params.add(new Float(tmpAttr.getGpsAlt()));
    	    	            params.add(tmpAttr.getImage());
    	    	            params.add((float)display.getHeight());
    	    	            params.add((float)display.getWidth());
    	    	            params.add(tmpAttr.getName());
    	    	            
    	    	            POIButton mButton = new POIButton(this, params);
    	    	            poiButtons.add(mButton);
    	    	        }
    					break;
    					
    				//If the types of interesting points is MUSEUM
    				case 2:
    					//Get the museums points
    					ArrayList<Museum> museums  = applicationData.getMuseums();
    	    	        for(Museum tmpMus : museums){
    	    	        	//Create the POI button with the museum information's
    	    	        	params =  new ArrayList<Object>();
    	    	            params.add(tmpMus.getId());
    	    	            params.add(new Coordinates(new Float(tmpMus.getGpsLat()),new Float(tmpMus.getGpsLong())));
//    	    	            params.add(new Coordinates(new Float(tmpMus.getGpsLong()),new Float(tmpMus.getGpsLat())));
    	    	            params.add(new Float(tmpMus.getGpsAlt()));
    	    	            params.add(tmpMus.getImage());
    	    	            params.add((float)display.getHeight());
    	    	            params.add((float)display.getWidth());
    	    	            params.add(tmpMus.getName());
    	    	            
    	    	            POIButton mButton = new POIButton(this, params);
    	    	            poiButtons.add(mButton);
    	    	        }
    					break;
    					
    				//If the types of interesting points is INFORMATION POINTS
    				case 3:
    					//Get the interesting points
    					ArrayList<InformationPoint> informationPoints = applicationData.getInformationPoints();
    					//Create the POI button with the in information point information's
    	    	        for(InformationPoint tmpInfo : informationPoints){
    	    	       		params =  new ArrayList<Object>();
    	    	            params.add(tmpInfo.getId());
    	    	            params.add(new Coordinates(new Float(tmpInfo.getGpsLat()),new Float(tmpInfo.getGpsLong())));
//    	    	            params.add(new Coordinates(new Float(tmpInfo.getGpsLong()),new Float(tmpInfo.getGpsLat())));
    	    	            params.add(new Float(tmpInfo.getGpsAlt()));
    	    	            params.add(tmpInfo.getImage());
    	    	            params.add((float)display.getHeight());
    	    	            params.add((float)display.getWidth());
    	    	            params.add(tmpInfo.getName());
    	    	            
    	    	            POIButton mButton = new POIButton(this, params);
    	    	            poiButtons.add(mButton);
    	    	        }
    					break;
    			}
    		}
    	}
    	for(POIButton tmp : poiButtons){
    		Coordinates coordinate = new Coordinates(new Float(myLocation.getLatitude()), new Float(myLocation.getLongitude()));
    		tmp.calculateDistance(coordinate, 0);
    	}
    	poiButtons = Quicksort.quicksort(poiButtons);
    	Log.i("poiLenght", Integer.toString(poiButtons.size()));
    	//Draw on the screen the interesting points
    	POIButton tmp = new POIButton(getApplicationContext());
        for(int i = (poiButtons.size()-1); i >= 0; i--){
        	tmp = poiButtons.get(i);
        	addContentView(tmp,  new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
		}
    }
    
    /**
     * Initialization the orientation
     */
    private void initializationSensor(){
        mySensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
        List<Sensor> mySensors = mySensorManager.getSensorList(Sensor.TYPE_ORIENTATION);
        
        //If the sensor work
        if(mySensors.size() > 0){
        	mySensorManager.registerListener(mySensorEventListener, mySensors.get(0), SensorManager.SENSOR_DELAY_NORMAL);
        	Toast.makeText(this, "Starting ORIENTATION Sensor", Toast.LENGTH_SHORT).show();
        }
        
        //Sensor not work
        else{
        	Toast.makeText(this, "No ORIENTATION Sensor", Toast.LENGTH_LONG).show();
        	finish();	
        }
    }

    /**
     * Initialization the location
     */
    private void initializationLocation(){
    	myLocationManager = new ARLocationManager(getApplicationContext());
    	myLocationManager.requestUpdates(2000, 10, locationListener);
    	Location location = myLocationManager.getLastLocation();
    	myLocation = location;
		updateARScreen();
    }
        
	/**
     * Sensor listener
     */
    private SensorEventListener mySensorEventListener = new SensorEventListener(){

		public void onAccuracyChanged(Sensor sensor, int accuracy) {}

		public void onSensorChanged(SensorEvent event) {
			// TODO Auto-generated method stub
			if(event.sensor.getType() == Sensor.TYPE_ORIENTATION){
				//Update the interesting points buttons with the actual location
				myAzimuth = event.values[0];
				myPitch = event.values[1];
				updateARScreen();
			}
		}
    };
    
    /**
     * Listener of the touchable screen
     */
    private OnTouchListener myTouchListener = new OnTouchListener() {
		
		public boolean onTouch(View v, MotionEvent event) {
			//Declarations and initializations
			int i;
			Poi dialogPoi = null;
			boolean found = false;
			Point p = new Point(Math.round(event.getRawX()), Math.round(event.getRawY()));
			
			i = 0;
			
			try {
				while(i < poiButtons.size() && !found){
					if(poiButtons.get(i).isClicked(p)){
						found = true;
						dialogPoi = applicationData.searchId(poiButtons.get(i).getId());
						if(dialogPoi == null){
							return false;
						}
					}
					i++;
				}
			} catch (Exception e) {
				e.printStackTrace();
				return false;
			}
			if(found){	
		    	PoiDialog poiDialog = new PoiDialog(ARactivity.this, getApplicationContext(), dialogPoi);
		    	poiDialog.show();
			}
			return false;
		}
	};
    
    
    /**
     * Location listener
     */
	private final LocationListener locationListener = new LocationListener() {
		//When change the location call the method which update the screen with the new dates 
		public void onLocationChanged(Location location) {
			
			myLocation = location;
			if(firstCalled){			//If the location is the first recived, adds the buttons to the screen
				addPOIButtons();
				firstCalled = false;
			}
			mDraw.drawSignal(2);
//			updateARScreen();
			Coordinates coordinate = new Coordinates(new Float(myLocation.getLatitude()), new Float(myLocation.getLongitude()));
//			Coordinates coordinate = new Coordinates(new Float(41.386356),new Float(2.164017));
			
			mRadar.updateCoordRadar(coordinate, myAzimuth, poiButtons, distance);
		}
		
		//When not find the position
		public void onProviderDisabled(String provider){
			Toast.makeText(getApplicationContext(), "No location found", Toast.LENGTH_LONG).show();
			mDraw.drawSignal(0);
			finish();
		}
		public void onProviderEnabled(String provider){ }
		public void onStatusChanged(String provider, int status, Bundle extras){
			if(status == LocationProvider.TEMPORARILY_UNAVAILABLE){
				mDraw.drawSignal(1);
			}else if(status == LocationProvider.AVAILABLE){
				mDraw.drawSignal(2);
			}else{
				mDraw.drawSignal(0);
			}
		}
	};
	
	/**
	 * The method which update the list of the interest point on the camera screen and the radar
	 * @param location keep the actual location
	 * @param azimuth Angle between the magnetic north direction and the Y axis, around the Z axis (0 to 359).
	 * @param pitch Rotation around X axis (-180 to 180)
	 */
	private void updateARScreen() {
		//When the location is ok
		try {
			//Update the interesting points buttons with the actual location
			if(myLocation != null){
				Coordinates coordinate = new Coordinates(new Float(myLocation.getLatitude()), new Float(myLocation.getLongitude()));
//				Coordinates coordinate = new Coordinates(new Float(41.386356),new Float(2.164017));			
				for (POIButton tmp : poiButtons) {
					if(distEnabled){
						tmp.updateDirection(coordinate,myAzimuth, myPitch, units, distance);
					} else{
						tmp.updateDirection(coordinate,myAzimuth, myPitch, units, 0);
					}
				}
			}
			//Update the radar
			mRadar.setAzimuth(myAzimuth);
			mRadar.invalidate();
//			mRadar.updateCoordRadar(coordinate, myAzimuth, poiButtons,distance);
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			Log.e("no loc", "-----------> Couldn't find the location!");
//			mRadar.setAzimuth(myAzimuth);
		}
	}

	
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		// TODO Auto-generated method stub
		super.onActivityResult(requestCode, resultCode, data);
		if(requestCode == 1){
			distance = data.getIntExtra("distance", distance);
			applicationData.setDistfilter(distance);
			if(myLocation !=  null){
				Coordinates coordinate = new Coordinates(new Float(myLocation.getLatitude()), new Float(myLocation.getLongitude()));
//			Coordinates coordinate = new Coordinates(new Float(41.386356),new Float(2.164017));
				if(distEnabled){
					mRadar.updateCoordRadar(coordinate, myAzimuth, poiButtons,distance);
				} else{
					mRadar.updateCoordRadar(coordinate, myAzimuth, poiButtons,0);
				}
			}
			updateARScreen();
		}
		else{
			Log.e("onActivityResult", "onActivityResult");
		}
	}
	
	
	@Override
	public void onBackPressed() {
//		super.onBackPressed();
		myLocationManager.removeUpdates(locationListener);
		mySensorManager.unregisterListener(mySensorEventListener);
		Intent intent = new Intent();
        intent.putExtra("com.ShowMe.Model.PoiList", applicationData);
        setResult(2,intent);
        mPreview.stopCamera();
        mPreview.releaseCamera();
		ARactivity.this.finish();
	}
	
	
	
	
}

